
package com.studiofortress.sf.util;

import com.studiofortress.sf.graphics.GLTexture;
import com.studiofortress.sf.graphics.GraphicsGL;
import java.awt.Dimension;
import java.util.TreeMap;

/**
 * This is a map that can store textures against characters. Using these stored
 * it can then be used to draw text to the screen.
 * 
 * @author Joseph Lenton - JosephLenton@StudioFortress.com
 */
public class TextGraphics extends TreeMap<Character, GLTexture>
{
    private int whitespaceWidth;
    private int lineSpaceHeight;

    public TextGraphics(int whitespaceWidth, int lineSpaceHeight)
    {
        this.whitespaceWidth = whitespaceWidth;
        this.lineSpaceHeight = lineSpaceHeight;
    }

    public int getLineSpaceHeight()
    {
        return lineSpaceHeight;
    }

    public int getWhitespaceWidth()
    {
        return whitespaceWidth;
    }

    public void setLineSpaceHeight(int lineSpaceHeight)
    {
        this.lineSpaceHeight = lineSpaceHeight;
    }

    public void setWhitespaceWidth(int whitespaceWidth)
    {
        this.whitespaceWidth = whitespaceWidth;
    }

    /**
     * Draws the given text with x and y being the bottom left corner of the
     * left most character on the last line.
     * @param g
     * @param text
     * @param x
     * @param y
     */
    public void draw(final GraphicsGL g, final String text, float x, float y)
    {
        draw( g, text, x, y, false );
    }

    public void draw(GraphicsGL g, String text, final float x, float y, final boolean isMidHandled)
    {
        draw( g, text, x, y, 1f, 1f, isMidHandled );
    }

    public void draw(final GraphicsGL g, final String text, float x, float y, float scale)
    {
        draw( g, text, x, y, scale, scale );
    }

    public void draw(final GraphicsGL g, final String text, float x, float y, float scaleX, float scaleY)
    {
        draw( g, text, x, y, scaleX, scaleY, false );
    }

    public void draw(GraphicsGL g, String text, final float x, float y, float scale, final boolean isMidHandled)
    {
        draw( g, text, x, y, scale, scale, isMidHandled );
    }

    public void draw(GraphicsGL g, String text, final float x, float y, float scaleX, float scaleY, final boolean isMidHandled)
    {
        if (isMidHandled ) {
            y -= getHeight( text )*scaleY / 2;
        }
        final String[] lines = text.split("\n");
        
        for (int i = lines.length-1; i >= 0; i--) {
            final String line = lines[i];
            final float maxHeight = drawLine(g, line, x, y, scaleX, scaleY, isMidHandled);
            y += maxHeight + getLineSpaceHeight()*scaleY;
        }
    }

    /**
     * This is the code run for drawing each indevidual line. It will presume
     * that the text given is a single line and does not include any end of line
     * characters.
     * @param g
     * @param line
     * @param x
     * @param y
     * @param isMidHandled True if the text will be midhandled along the x-axis only.
     * @return The height of the tallest letter in the line of text drawn. Does not include line spacing.
     */
    protected float drawLine(final GraphicsGL g, final String line, float x, float y, float scaleX, float scaleY, boolean isMidHandled)
    {
        if (isMidHandled) {
            x -= getWidth( line )*scaleX / 2;
        }

        float maxHeight = 0;

        for (int j = 0; j < line.length(); j++) {
            final char c = line.charAt( j );
            if (c == ' ') {
                x += getWhitespaceWidth()*scaleX;
            } else {
                GLTexture tex = get( c );
                drawLetter(g, tex, x, y, scaleX, scaleY);
                x += tex.getWidth()*scaleX;
                maxHeight = Math.max( maxHeight, tex.getHeight() );
            }
        }
        
        return maxHeight*scaleX;
    }
    
    protected void drawLetter(final GraphicsGL g, final GLTexture letter, final float x, final float y, float scaleX, float scaleY)
    {
        g.drawTexture( letter, x, y, letter.getWidth()*scaleX, letter.getHeight()*scaleY);
    }

    /**
     * Looks for the character given and returns the image that is used to draw
     * it. If the image is not found then an exception will be thrown.
     * @param c The character to retrieve the texture for.
     * @return The texture stored against the given character.
     */
    protected GLTexture getLetter(char c)
    {
        GLTexture letter = get( c );
        if (letter == null) {
            throw new IllegalArgumentException("Character image was not found: " + c);
        }

        return letter;
    }

    /**
     * @param text The text to calculate the width of, cannot be null.
     * @return The maximum width of the given text when it is rendered.
     */
    public int getWidth(String text)
    {
        int maxWidth = 0;

        for (String line : text.split("\n")) {
            int lineWidth = 0;
            for (int i = 0; i < line.length(); i++) {
                lineWidth += getCharWidth( line.charAt(i) );
            }
            maxWidth = Math.max(maxWidth, lineWidth);
        }
        
        return maxWidth;
    }

    /**
     * @param text The text to calculate the height of, cannot be null.
     * @return The maximum height of the given
     */
    public int getHeight(String text)
    {
        int maxHeight = 0;
        boolean wasLastLine = false;

        for (String line : text.split("\n")) {
            if (wasLastLine) {
                maxHeight += getLineSpaceHeight();
            } else {
                wasLastLine = true;
            }

            int lineHeight = 0;
            for (int i = 0; i < line.length(); i++) {
                lineHeight = Math.max( lineHeight, getCharHeight( line.charAt(i) ) );
            }

            maxHeight = maxHeight + lineHeight;
        }

        return maxHeight;
    }

    public int getCharWidth(char c)
    {
        if (c == ' ') {
            return whitespaceWidth;
        } else {
            return getLetter(c).getWidth();
        }
    }

    /**
     * @param c The character to calculate the height of.
     * @return The height of the character given, or 1 if a space was given.
     */
    public int getCharHeight(char c)
    {
        if (c == ' ') {
            return 1;
        } else {
            return getLetter(c).getHeight();
        }
    }

    /**
     * Returns the size of the bounds surrounding the given piece of text.
     * @param text The text to get the bounds for, cannot be null.
     * @return The size of the bounds around the given piece of text.
     */
    public Dimension getSize(String text)
    {
        int maxWidth = 0;
        int maxHeight = 0;
        boolean wasLastLine = false;

        for (String line : text.split("\n")) {
            if (wasLastLine) {
                maxHeight += getLineSpaceHeight();
            } else {
                wasLastLine = true;
            }
            int lineWidth = 0;
            int lineHeight = 0;
            for (int i = 0; i < line.length(); i++) {
                lineWidth += getCharWidth( line.charAt(i) );
                lineHeight = Math.max( lineHeight, getCharHeight( line.charAt(i) ) );
            }
            maxWidth = Math.max(maxWidth, lineWidth);
            maxHeight = maxHeight + lineHeight;
        }

        return new Dimension(maxWidth, maxHeight);
    }
}
